package muse.proxy;

/**
 * ProxyReceiver.java
 * Thread che si occupa della ricezione del flusso RTP proveniente dal client;
 * i frame ricevuti sono immagazzinati in un buffer
 * @author Ambra Montecchia
 * @version 1.2
 * */

import javax.media.protocol.DataSource;
import javax.media.rtp.ReceiveStreamListener;
import javax.media.rtp.event.ByeEvent;
import javax.media.rtp.event.NewReceiveStreamEvent;
import javax.media.rtp.event.ReceiveStreamEvent;
import javax.media.control.BitRateControl;
import javax.media.Format;
import javax.media.Manager;
import javax.media.Player;
import javax.swing.JFrame;

import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.InetAddress;
import unibo.core.rtp.RTPReceiverPS;
import unibo.core.rtp.RTPSenderPS;
import unibo.core.multiplexer.PlayerMultiplexer;
import unibo.core.multiplexer.RTPMultiplexer;
import unibo.core.parser.RTPParser;
import unibo.core.thread.MultiplexerThreadPS;
import unibo.core.thread.MuseMultiplexerThread;
import unibo.core.thread.ParserThreadPS;
import unibo.core.CircularBuffer;
import parameters.Parameters;
import util.Logger;
import debug.DebugConsolle;

public class ProxyReceiver implements ReceiveStreamListener{

	//ricevitore del flusso RTP proveniente dal server
	private RTPReceiverPS receiver;
	//parser per la lettura del flusso ricevuto dal server
	private ParserThreadPS parser;
	//array contenente informazioni sul formato delle tracce che compongono il file
	private Format[] formats;
	//indirizzo IP del server, noto staticamente al client
	private String serverIP;
	//porta del server attraverso cui avviene la trasmissione del flusso RTP
	private int serverPort;
	//consolle di debug
	private DebugConsolle dc;
	//controller per bitrate
	private BitRateControl brCtrl;
	//porta di ricezione
	int port;
	
	private Thread runner;
	
	private ProxySender sender;
	
	private Player player;
	
	private RTPMultiplexer mux;
	private MuseMultiplexerThread muxThread;
	private DataSource ds;
	private CircularBuffer buffer;
	private String clientIP;
	private int clientPort;
	private int sendingP;
	
	/**
	 * Costruttore
	 * */
	public ProxyReceiver(String sIP, int sPort, DebugConsolle consolle, int recPort, String clientIP, int clientPort, int sendingP){
		
		serverIP = sIP;
		serverPort = sPort;
		dc = consolle;
		port = recPort;
		try{
			receiver = new RTPReceiverPS(port);
		}
		catch(Exception e){
			System.out.println("Impossibile inizializzare la ricezione da parte del proxy: "+e.getMessage());
			System.exit(1);
		}
		parser = null;
		brCtrl = null;
		this.clientIP = clientIP;
		this.clientPort = clientPort;
		this.sendingP = sendingP;
	}
	
	/**
	 * Metodo per la ricezione del flusso RTP proveniente dal server
	 * */
	public void receive(){
		
		try{
			
			/** RECEIVER **/
			InetAddress servAddr = InetAddress.getByName(serverIP);
			receiver.setSender(servAddr, serverPort);
			receiver.setBufferLength(0);
			receiver.addReceiveStreamEventListener(this);
			dc.debugMessage("Proxy receiver: attesa ricezione stream...");
			System.out.println("Proxy receiver: attesa ricezione stream...");
			//ricezione dello stream RTP
			ds = receiver.receiveData();
			brCtrl = (BitRateControl)ds.getControl("javax.media.control.BitRateControl");
			dc.debugMessage("Proxy receiver: datasource ricevuto...");
			System.out.println("Proxy receiver: datasource ricevuto...");
			
			/** PARSER **/
			//lettura e memorizzazione dei frame che compongono lo stream RTP
			dc.debugMessage("Proxy receiver: Inizio memorizzazione...");
			System.out.println("Proxy receiver: attesa ricezione stream...");
			RTPParser rtpPs = new RTPParser(ds);
			formats = rtpPs.getTracksFormat();
			parser = new ParserThreadPS(rtpPs, Parameters.PROXY_BUFFER);
			CircularBuffer[] b =parser.getOutputBufferSet();
			buffer = b[0];
			
			/** MULTIPLEXER **/
			mux = new RTPMultiplexer(formats);
			muxThread = new MuseMultiplexerThread(mux,b,null,5);
			
			/** SENDER **/
			sender = new ProxySender(clientIP,clientPort,dc,sendingP,mux,muxThread);			
			parser.start();
		}
		catch(Exception e){
			Logger.write("Errore in ricezione da parte del proxy: "+e.getMessage());
			System.exit(1);
		}
		
	}
	
	/**
	 * Metodo che restituisce il riferimento all'insieme di buffer utilizzati dal proxy
	 * */
	public CircularBuffer[] getBuffers(){
		
		CircularBuffer[] buffers = parser.getOutputBufferSet();
		return buffers;
	}
	
	/**
	 * Metodo che restituisce un array contenente i formati delle tracce che costituiscono il file
	 * */
	public Format[] getFormats(){
		
		return formats;
	}
	
	/**
	 * Metodo per la chiusura del ricevitore
	 * */
	public void closeAll()
	{
		if(muxThread!=null) muxThread.close();
		if(mux!=null) mux.close();
		if(parser!=null) parser.close();
		if(ds!=null) ds.disconnect();
		if(receiver!=null) receiver.close();
		if(sender!=null)sender.close();
	}
	
	/**
	 * Restituisce la bitrate del file trasmesso
	 * */
	public int getBitRate(){
		if(brCtrl != null){
			return brCtrl.getBitRate();
		}
		else
			return -1;
	}

	public void update(ReceiveStreamEvent event) {
		if (event instanceof ByeEvent)
		{
			System.out.println("___Bye Message in PROXY RECEIVER___\n");
			
			if(parser!=null)
			{
				parser.close();
				parser=null;
			}
			if(ds!=null){
				ds.disconnect();
				ds=null;
			}
			
			com.sun.media.ExtBuffer frame=new com.sun.media.ExtBuffer();
			frame.setEOM(true);
			try{ Thread.sleep(100); }
			catch(InterruptedException e){ System.err.println(e); }
			buffer.setFrame(frame);

			while(!muxThread.isClosed())
			{
				try{ Thread.sleep(10); }
				catch(InterruptedException e){ System.err.println(e); }
			}

			closeAll();
			
			System.out.println("Proxy: terminata trasmissione");
			dc.debugMessage("Proxy: terminata trasmissione");
		}
	}
	
	public ProxySender getSender(){
		return sender;
	}
	
	public int getFrameOnBuffer(){
		if(buffer==null){ 
			System.out.println("Numero di frame presenti nel buffer: "+0);
			return 0;
		}
		System.out.println("Frame nel buffer: "+buffer.contatore);
		return buffer.contatore;
	}
}
